## JDK1.8 新特性

- 是java语言开发的一个很重要的版本，Oracle 公司于 2014 年3月18日发布的。
- 支持
  - Lambda 表达式
  - 函数式接口 ***
  - 新的Stream API ***
  - 新的日期时间 API

### 1. Lambda表达式

- 特殊的匿名内部类，语法更加的简洁

- 基本语法：

  - 函数式接口  变量名 = (参数列表)->{}
    - Runnable
  - Compartor

- 注意问题

  - 形参列表的数据类型可以自动推断。
  - 形参为空，保留小括号。
  - 形参只有一个，可以省略小括号。
  - 如果执行语句只有一句，且没有返回值，可以省略大括号。
  - 如果执行语句只有一句，且有返回值，如果省略大括号，那么必须return 语句一起省略。
  - Lambda 不会生成一个单独的内部类文件。

- ```java
  package com.qf.lambda;
  
  import java.util.Comparator;
  import java.util.TreeSet;
  
  /**
   * Lambda 表达式 1：是一些特殊的接口的匿名子类对象的特殊的表达方式。
   * 		特殊的接口：接口只能有一个抽象的方法没有实现。
   * 		可以让代码变得很简洁。
   * 
   * Lambda 的语法：
   * 	父接口类型 父接口引用 = (形参列表)->{方法体}
   * 	
   * 特殊的语法：
   * 	1：如果形参列表只有一个参数，那么小括号可以省略。形参的类型也可以省略。
   * 	2: 如果方法体只有一句代码，也可以省略大括号。
   * 		有返回值：如果省略大括号，return 也得省略。
   * 		无返回值：直接省略大括号即可。
   *	3：形参列表为空，必须有小括号。
   * 
   *
   */
  public class TestLambda {
  
  	public static void main(String[] args) {
  		// 匿名内部类。
  		Runnable runnable = new Runnable() {
  			@Override
  			public void run() {
  				System.out.println("run");
  			}
  		};
  		new Thread(runnable).start();
  
  		// Lambda
  		Runnable run = () -> {
  			System.out.println("run1");
  		};
  		new Thread(run).start();
  		
  		new Thread(() -> {
  			System.out.println("run2");
  		}).start();
  		
  //		形参列表只有一个的情况
  		Comparable<Integer> com = o -> 1;//有返回值的写法。
  		MyInterface interface1 = o->System.out.println(o);//无返回值的写法。
  		
  		
  		Comparator<Integer> com1 = new Comparator<Integer>() {
  			public int compare(Integer o1, Integer o2) {
  				return o1-o2;
  			};
  		};
  		TreeSet<Integer> set = new TreeSet<>(com1);
  		
  		Comparator<Integer> com2 = (o1,o2)->o1-o2;
  		
  		TreeSet<Integer> set1 = new TreeSet<>(com2);
  		
  		TreeSet<Integer> set2 = new TreeSet<>((o1,o2)->o1-o2);
  			
  	}
  }
  
  interface MyInterface{
  	void test(Object o);
  }
  ```

- 

### 2. 函数式接口 ***

- **有且仅有一个抽象方法的接口**，称为函数式接口。只有函数式接口才能使用 Lambda 表达式。

- JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景，它们主要在 java.util.function 包中被提供。

- @FunctionalInterface 注解用于检测接口是否符合函数式接口

- 常见的函数式接口

  - **Supplier 生产型接口**
    - java.util.function.Supplier 接口仅包含一个无参的方法： T get() 。用来获取一个泛型参数指定类型的对象数据。Supplier接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据。
    - **无参，有返回**
  - **Consumer 消费型接口**
    - java.util.function.Consumer 接口则正好与Supplier接口相反，它不是生产一个数据，而是**消费**一个数据（至于具体怎么消费(使用), 需要自定义(输出，计算…） 其数据类型由泛型决定。Consumer 接口中包含抽象方法 void accept(T t) ，意为消费一个指定泛型的数据。
    - **有参，无返回**
  - **Predicate 断言接口**
    - 有时候我们需要对某种类型的数据进行判断，从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。Predicate 接口中包含一个抽象方法： boolean test(T t) 。
    - **有参，返回boolean**
  - **Function 函数型接口**
    - java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据，前者称为前置条件，后者称为后置条件。Function 接口中最主要的抽象方法为： R apply(T t) ，根据类型T的参数获取类型R的结果。
    - **有参，有返回**

- 案例：

  - Supplier 生产随机的钱，Consumer 负责不同钱的消费。

  - Predicate 人名过滤功能。

  - Function 大小写的转换。

  - ```java
    package com.qf.lambda;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.function.Supplier;
    
    /**
     * 函数式接口：
     * 	特点：如果一个接口中只有一个抽象方法，那么该接口就称为函数式接口。
     * 		只有函数式接口才能使用 Lambda 表达式。
     * 
     * JDK 提供的函数式接口： java.util.function 都在这个包下。
     * 	@FunctionalInterface 注解用来检查 接口是否满足 函数式接口。
     * 	1：供给型接口：Supplier 
     * 		方法：无参有返回。
     * 		 T get(); 
     * 		通过该接口可以获取 T 类型的数据。
     * 	2：消费型接口：Consumer 
     * 		方法：有参无返回。
     * 		void accept(T t);
     * 		可以消费 T 类型的数据。
     * 	3：断言型接口：Predicate 
     * 		方法：内容过滤，有参，有返回boolean。
     * 		boolean test(T t);
     * 		判断 T 类型的 数据是否满足你的的要求。
     * 	4：函数型接口：Function 
     * 		方法：做某些功能的实现，有参，有返回。
     * 		 R apply(T t);
     * 		对 T 类型的数据，进行加工，返回 R 类型的数据。
     */
    public class TestFunctionInterface {
    
    	public static void main(String[] args) {
    //		1：供给型接口：Supplier 
    		int[] array = getArray(new Supplier<Integer>() {
    			public Integer get() {
    				return (int)(Math.random() * 100);
    			}
    		}, 10);
    		
    		System.out.println(Arrays.toString(array));
    //		Lambda
    		array = getArray(()->(int)(Math.random() * 100), 10);
    		System.out.println(Arrays.toString(array));
    		
    //		2：消费型接口：Consumer 
    		happy(new Consumer<Integer>() {
    			@Override
    			public void accept(Integer t) {
    				System.out.println("佳欣花费了："+ t +"元去看了一段让自己上火的舞蹈！");
    			}
    		}, 1000);
    		
    		happy(t->System.out.println("佳欣花费了："+ t +"元去看了一段让自己上火的舞蹈！"), 1000);
    		
    		
    //		3：断言型接口：Predicate 
    		List<Integer> list = filterNums(new Predicate<Integer>() {
    			public boolean test(Integer t) {
    				return t%2==0;
    			}
    		}, array);
    		System.out.println(list);
    		
    		List<Integer> list1 = filterNums(t->t%2==0, array);
    		System.out.println(list1);
    		
    //		函数型接口：Function 
    		String case1 = toUpperCase(new Function<String, String>() {
    			@Override
    			public String apply(String t) {
    				return t.toUpperCase();
    			}
    		}, "abc");
    		
    		System.out.println(case1);
    		
    		String case2 = toUpperCase(t->t.toUpperCase(), "2134fjlerjoJLFJDLKdlkjfd");
    		
    		System.out.println(case2);
    		
    	}
    	
    //	通过 Supplier 接口，获取若干个(count)整数，组成一个数组。
    	public static int[] getArray(Supplier<Integer> supplier,int count){
    		int[] arr = new int[count];
    		
    		for (int i = 0; i < count; i++) {
    			arr[i] = supplier.get();
    		}
    		return arr;
    	}
    	
    //	消费钱去happy
    	static void happy(Consumer<Integer> consumer,int money){
    		consumer.accept(money);
    	}
    	
    //	将一个数组中的内容的偶数部分以List 形式返回。
    	static List<Integer> filterNums(Predicate<Integer> predicate,int[] arr){
    		List<Integer> list = new ArrayList<Integer>();
    		
    		for (int i = 0; i < arr.length; i++) {
    //			元素满足要求，添加到List 中。
    			if(predicate.test(arr[i])){
    				list.add(arr[i]);
    			}
    		}
    		return list;
    	}
    	
    //	将任意的字符串转换为大写。
    	static String toUpperCase(Function<String, String> fun,String str){
    		return fun.apply(str);
    	}
    }
    ```

  - 

### 3. 方法引用（了解）

- 方法引用：Lambda表达式的简写形式。
- 如果Lambda表达式方法体中只是调用一个特定的已经存在的方法，则可以使用方法引用。
- 常用形式：
  - 对象::实例方法
  - 类::静态方法
  - 类::实例方法
  - 类::new

```java
package com.qf.day28;

import java.util.Comparator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 方法引用：Lambda表达式的简写形式。
 * 如果Lambda表达式方法体中只是调用一个特定的已经存在的方法，则可以使用方法引用。
 */
public class MethodRef {
    public static void main(String[] args) {
        //形式1: 对象::实例方法
        Consumer<String> consumer1=s-> System.out.println(s);
        //（1）Lambda表达式只能包含一个方法调用 (2) 调用的方法的参数和返回值和接口中方法一致。
        Consumer<String> consumer2=System.out::println;
        consumer1.accept("hello");
        consumer2.accept("world");
        //形式2：类::静态方法
        //（1）Lambda表达式只能包含一个方法调用 (2)调用的方法的参数和返回值和接口中方法一致。
        Comparator<Integer> com1=(o1,o2)->Integer.compare(o1, o2);
        Comparator<Integer> com2=Integer::compare;
        //形式3：类::实例方法
        //（1）Lambda表达式只能包含一个方法调用 (2)一个参数作为调用者，其他作为参数
        Comparator<Integer> com3=(o1,o2)->o1.compareTo(o2);
        Comparator<Integer> com4=Integer::compareTo;
        //形式4：类::new
        //（1）Lambda表达式只能包含一个构造方法调用  (2)调用的构造方法的参数和接口中方法参数一致。
        Supplier<Student> sup1=()->new Student();
        Supplier<Student> sup2=Student::new;
        Student s1 = sup1.get();
        Student s2 = sup2.get();

        MySupplier<Student> sup3=(name,age)->new Student(name,age);
        MySupplier<Student> sup4=Student::new;

        Student s3 = sup3.get("张三", 20);
        Student s4 = sup4.get("李四", 22);
        //形式5:元素类型[]::new
        //（1）Lambda表达式只能包含一个数组的创建。（2）参数作为数组的长度
        Function<Integer,Student[]> fun1=n->new Student[n];
        Function<Integer,Student[]> fun2=Student[]::new;
        Student[] students = fun2.apply(5);
        System.out.println(students.length);
    }
}

```

### 4. Stream ***(Lambda应用)

- Stream 与集合类似，但集合中保存的是数据，而Stream中保存的是对集合或者数组的数据的操作。所以 **Stream是对数据集操作的集合**

- Stream 特点

  - 不会存储元素。
  - 不会改变源对象，它只会返回一个持有结果的新的Stream。
  - 操作的是延迟执行的，等需要结果的时候才会执行。

- **Stream 体系**

  - BaseStream 接口
    - 四个子接口 DoubleStream、IntStream、LongStream、Stream

- Stream的使用步骤

  - 创建流：新建一个流。
  - 中间操作：在一个或者多个步骤，将初始的Stream转换到另外一个Stream。
  - 终止操作：使用一个终止操作来产生一个结果，该操作会强制之前的延迟操作立即执行，然后Stream就不能使用了。

- Stream的创建方式：

  - **使用Collection接口中stream()或parallelStream()。**

  - **通过Arrays类的stream()方法。**

  - 通过Stream接口的of()、iterate()、generate()方法。

    - Stream.iterate：迭代流  limit() 限制次数

  - 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法。

    - 范围控制

  - ```java
    package com.qf.stream;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.UnaryOperator;
    import java.util.stream.IntStream;
    import java.util.stream.Stream;
    
    import com.qf.util.MyUtil;
    
    /**
     * Stream: 流 1：Stream 流 和 IO 流没有任何关系。 IO 流 是信息的通道。通过通道流对象，可以实现对数据的读写操作。
     * 主要是针对容器中的元素的操作的功能的增强。 1：批量处理容器中的数据。 2：聚合操作（max、min、sum...avg count） 2: Stream
     * 支持了 并行流（多线程）处理 容器中的数据。
     * 
     * 3：大量的使用函数式接口，使用 Lambda 表达式。
     * 
     * 4：Stream 流 分为两类 1：中间操作。各种对数据的处理的操作。 一般会采用链式编程。 2：终结操作。将前面的所有的中间操作生效。
     * 执行终结操作，Stream就不可用了。 5：对流的操作分为了2类 1：有状态操作 具有存储功能的操作，后面的数据的处理要依赖前面处理的数据。
     * 2：无状态操作 不具有存储功能的操作，后面的数据的处理不依赖前面处理的数据。
     * 
     * 6：流的使用流程： 1：获取流-->n次 中间操作-->终结操作
     * 
     * 7：流的获取 
     * 		1：使用 Collection 接口中的 stream() 方法。(串行流)
     * 		2：使用 Collection 接口中的 parallelStream();并行流。底层使用了多线程。
     * 			优点：效率高。
     * 			缺点：数据的处理的顺序可能错乱。
     * 
     * 		3: Arrays类的stream()方法
     * 		4: Stream接口的of()、iterate()、generate()方法。
     * 		5:通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法。
     * 
     */
    public class TestStream {
    
    	public static void main(String[] args) {
    		// 1：使用 Collection 接口中的 stream() 方法。
    		List<String> list = Arrays.asList("hello", "break", "continue", "annotation", "synchronization");
    		Stream<String> stream = list.stream();
    //		终结遍历操作。
    		list.forEach(s->System.out.println(s));
    		
    //		2：使用 Collection 接口中的 parallelStream();并行流。底层使用了多线程。
    		Stream<String> stream2 = list.parallelStream();
    		list.forEach(s->System.out.println(s));
    		
    		Object[] array = list.toArray();
    //		list.toArray(a)
    //		3：Arrays类的stream()方法
    		Stream<Object> stream3 = Arrays.stream(array);
    //		Arrays.stream(array, startInclusive, endExclusive)
    		stream3.forEach(s->System.out.println(s));
    		
    //		4: Stream接口的of()、iterate()、generate()方法。
    		Stream<String> stream4 = Stream.of("a","b");
    //		Stream 处理的数据源来自于 供给接口。
    		Stream<Integer> stream5 = Stream.generate(()->MyUtil.random(0, 100));
    //		stream5.limit(100).forEach(s->System.out.println(s));
    		System.out.println("-------------");
    		Stream<Integer> stream6 = Stream.iterate(1, t->t+1);
    		
    		//1 
    		Stream<Integer> stream7 = Stream.iterate(1, new UnaryOperator<Integer>(){
    			@Override
    			public Integer apply(Integer t) {
    				System.out.println(t);
    				return t+1;
    			}});
    	
    		
    		stream7.limit(10).forEach(s->System.out.println(s));
    //		
    		stream6.limit(30).forEach(s->System.out.println(s));
    		
    //		IntStream
    		IntStream range = IntStream.range(30, 60);
    		range.forEach(s->System.out.println(s));
    		
    	}
    }
    ```

  - 

- Stream 的中间操作

  - filter、limit、skip、distinct、sort

  - map

  - parallel

  - ```java
    package com.qf.stream;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Stream;
    
    import com.qf.util.MyUtil;
    
    /**
     * 流的中间操作
     * 	1：中间操作之后，会产生新的Stream 对象。可以继续中间操作，或者是终结操作。
     * 		返回新的Stream 对象，对开始的Stream 对象没有影响。
    - filter、
    	limit、
    	skip、
    	distinct、
    	sort
    - map
    - parallel *
     */
    public class StreamMidTest1 {
    
    	public static void main(String[] args) {
    		List<String> list = Arrays.asList("int","byte","boolean","float","double","char","short","long");
    		
    		Stream<String> stream = list.stream();
    		
    //		中间操作。
    //		filter 过滤功能
    		stream.filter(s->s.length()<=5).forEach(s->System.out.println(s));
    		
    		System.out.println("-----------------");
    //		流一旦终结操作之后，就不能使用了。
    		Stream<String> stream1 = list.stream();
    //		limit 限制|极限  取数据集中的前n个。
    		stream1.filter(s->s.length()<=5).limit(5).forEach(s->System.out.println(s));
    		System.out.println("-----------------");
    //		skip 跳过前n 个数据。
    		Stream<String> stream2 = list.stream();
    		stream2.skip(2).forEach(s->System.out.println(s));
    		
    		System.out.println("-----------------");
    //		distinct、去重(需要重写 equals 和 hashCode 方法)
    		List<Student> lists = new ArrayList<Student>();
    		lists.add(new Student("A",10));
    		lists.add(new Student("A",10));
    		lists.add(new Student("A",10));
    		
    		Stream<Student> stream3 = lists.stream();
    		stream3.distinct().forEach(s->System.out.println(s));
    		
    //		sorted() 排序 要求 类型要实现内部比较器接口。或者指定外部比较器对象。
    //		System.out.println("-----------------");
    		List<Student> list3 = new ArrayList<Student>();
    		list3.add(new Student("A",MyUtil.random(0, 100)));
    		list3.add(new Student("A",MyUtil.random(0, 100)));
    		list3.add(new Student("A",MyUtil.random(0, 100)));
    		
    		Stream<Student> stream4 = list3.stream();
    		stream4.sorted().forEach(s->System.out.println(s));
    		
    		
    //		map  对容器中的所有的元素进行统一的处理，并返回处理的结果。
    //		将字符串集，转换为 学生对象集
    		List<String> list2 = Arrays.asList("tom:10","tom1:10","tom2:10","tom3:10","tom4:10");
    		
    		list2.stream().map(s->{
    			String[] split = s.split(":");
    			String name = split[0];
    //			Integer.parseInt(split[1])
    			int age = Integer.valueOf(split[1]);
    			return new Student(name,age);
    		}).forEach(s->System.out.println(s));
    		
    //		parallel  将串行流，转换为并行流。
    //		list2.parallelStream().limit(maxSize)
    		
    	}
    
    }
    
    class Student implements Comparable<Student>{
    	private String name;
    	private int age;
    	public Student(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + "]";
    	}
    	@Override
    	public int hashCode() {
    		final int prime = 31;
    		int result = 1;
    		result = prime * result + age;
    		result = prime * result + ((name == null) ? 0 : name.hashCode());
    		return result;
    	}
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj)
    			return true;
    		if (obj == null)
    			return false;
    		if (getClass() != obj.getClass())
    			return false;
    		Student other = (Student) obj;
    		if (age != other.age)
    			return false;
    		if (name == null) {
    			if (other.name != null)
    				return false;
    		} else if (!name.equals(other.name))
    			return false;
    		return true;
    	}
    	@Override
    	public int compareTo(Student o) {
    		return this.age - o.age;
    	}
    }
    ```

  - 

- Stream 的终止操作

  - forEach、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、toArray

  - reduce 统计工资

  - collect  收集人名

  - ```java
    package com.qf.stream;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    import java.util.stream.Collector;
    import java.util.stream.Collectors;
    
    /**
     * 流的终结操作:
     * 	1: 流的终结操作之后，流就不能再使用了。
     * 	2：分类
     * 		短路：找到满足要求的元素就立即结束。
     * 		非短路：要遍历所有的容器中的元素。
     * 
     *  - forEach、遍历的
     *  min：求容器中的最小值。
     *  max：最大值
     *  count：统计个数。
     *  anyMatch：短路，是否有匹配的内容。
     *  allMatch：短路，是有所有的都匹配。
     *  noneMatch：是否都不匹配
     *  findFirst：获取到第一个元素。
     *  findAny：获取到任意一个元素。
     *  toArray：将容器中的数据保存到一个数组中。
     *  reduce 统计。。统计工资
     *  collect 收集   收集人名
     */
    public class StreamEndTest {
    
    	@SuppressWarnings("unchecked")
    	public static void main(String[] args) {
    		List<Student> stus = new ArrayList<Student>();
    		stus.add(new Student("刘备",30));
    		stus.add(new Student("曹操",32));
    		stus.add(new Student("孙权",25));
    		stus.add(new Student("大乔",22));
    		stus.add(new Student("小乔",16));
    		stus.add(new Student("周瑜",26));
    		stus.add(new Student("张飞",29));
    //		判空 利器。保证了得到的结果一定不是 null。
    //		min：求容器中的最小值。
    		Optional<Student> result = stus.stream().min((s1,s2)->s1.getAge()-s2.getAge());
    //		获取里面的内容
    		System.out.println(result.get());
    		
    //		 *  max：最大值
    //		 *  count：统计个数。
    		long count = stus.stream().count();
    		System.out.println(count);
    		
    //		 *  anyMatch：短路，是否有匹配的内容。
    		boolean match = stus.stream().anyMatch(s->s.getAge()==16);
    		System.out.println(match);
    //		 *  allMatch：短路，是有所有的都匹配。
    		match = stus.stream().allMatch(s->s.getAge()>=16);
    		System.out.println(match);
    //		 *  noneMatch：是否都不匹配
    		
    //		 *  findFirst：获取到第一个元素。
    		Optional<Student> first = stus.stream().findFirst();
    		System.out.println(first.get());
    //		 *  findAny：获取到任意一个元素。
    		Optional<Student> any = stus.stream().findAny();
    		System.out.println(any.get());
    //		 *  toArray：将容器中的数据保存到一个数组中。
    		Object[] array = stus.stream().toArray();
    		
    //		 *  reduce 统计。。统计年龄总和
    		Optional<Integer> reduce = stus.stream().map(s->s.getAge()).reduce((age1,age2)->age1+age2);
    		System.out.println(reduce.get());
    		//*  collect 收集   收集人名 -->list
    		List<String> names = stus.stream().map(s->s.getName()).collect(Collectors.toList());
    		
    		System.out.println(names);	
    	}
    }
    ```

  - 

### 5. 新的时间API

- 之前的时间api存在的问题：

  - 线程非安全
    - 多个线程同时将一个日期字符串格式化成一个日期对象存在线程安全问题。
  - 时间可变
    - 可以对日期对象修改它的属性值。

- java.time.LocalDateTime

  - 本地日期时间，不可变的
  - 创建对象：
    - LocalDateTime.now()
    - LocalDateTime.of(......)
  - 实例方法：
    - minus...
    - plus....
    - get.....
    - with....

- Instant：时间戳 

  - Instant类在Java日期与时间功能中，表示了时间线上一个确切的点，定义为距离初始时间的时间差（初始时间为GMT 1970年1月1日00:00）
  - 创建对象：
    - Instant.now()   格林尼治时间
    - Instant.ofEpochMilli(milliSeconds)
  - toEpochMilli()
  - minusMillis
  - plusMillis

- ZoneId 时区

  - ZoneId.getAvailableZoneIds()
  - ZoneId.systemDefault()

- Date--Instant--LocalTime （将原来的Date转换为LocalTime）

  ```java
  Date date = new Date();
  Instant instant = date.toInstant();
  LocalDateTime time = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
  ```

- LocalTime--Instant--Date

  ```java
  LocalDateTime time = LocalDateTime.now();
  Instant instant = time.atZone(ZoneId.systemDefault()).toInstant();
  Date date = Date.from(instant);
  ```

- DateTimeFormatter

  - 日期和字符串之间的相互转换

  ```java
  LocalDateTime time = LocalDateTime.now();
  
  DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  String result = dtf.format(time);
  		
  String str = "2022-04-16 18:31:43";
  LocalDateTime time2 = LocalDateTime.parse(str,dtf);
  ```

  

`Stream` 是 Java 8 引入的一个强大功能，它提供了一种高级迭代器，允许你以声明式方式处理数据集合。`Stream` 流可以看作是一个数据管道，通过它可以对数据集合进行一系列的操作，如过滤、排序、转换、聚合等。

### Stream 的主要特性

1. **管道式操作**：Stream 的操作可以链式调用，形成一个管道，每个操作的输出作为下一个操作的输入。
2. **惰性求值**：Stream 的操作不会立即执行，直到调用终止操作（如 `collect`、`forEach`、`count` 等）时才会开始执行。
3. **不可变性**：Stream 的操作不会修改原始数据源，而是返回一个新的 Stream 或结果。
4. **支持并行操作**：Stream 支持并行操作，可以通过 `parallelStream` 方法将 Stream 转换为并行 Stream，从而利用多核处理器提高性能。

### Stream 的创建

Stream 可以通过多种方式创建，常见的有：

- 从集合创建
- 从数组创建
- 从文件创建
- 使用 `Stream` 的静态方法创建

#### 示例：创建 Stream

```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamCreation {
    public static void main(String[] args) {
        // 从集合创建 Stream
        List<String> list = Arrays.asList("apple", "banana", "cherry");
        Stream<String> streamFromList = list.stream();

        // 从数组创建 Stream
        String[] array = {"apple", "banana", "cherry"};
        Stream<String> streamFromArray = Arrays.stream(array);

        // 使用 Stream 的静态方法创建 Stream
        Stream<String> streamFromStatic = Stream.of("apple", "banana", "cherry");

        // 从文件创建 Stream（需要引入 java.nio.file 包）
        // Stream<String> streamFromFile = Files.lines(Paths.get("file.txt"));
    }
}
```

### Stream 的操作

Stream 的操作可以分为中间操作和终止操作：

- **中间操作**：返回一个新的 Stream，可以链式调用。常见的中间操作有：
  - `filter`：过滤 Stream 中的元素
  - `map`：对 Stream 中的元素进行映射
  - `sorted`：对 Stream 中的元素进行排序
  - `distinct`：去除 Stream 中的重复元素
- **终止操作**：返回一个结果或副作用，结束 Stream 的处理。常见的终止操作有：
  - `forEach`：对 Stream 中的每个元素执行操作
  - `collect`：将 Stream 中的元素收集到集合中
  - `count`：统计 Stream 中元素的数量
  - `reduce`：对 Stream 中的元素进行归并操作

#### 示例：Stream 操作

```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamOperations {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apple");

        // 中间操作：过滤、映射、去重、排序
        List<String> result = fruits.stream()
                .filter(fruit -> fruit.startsWith("a")) // 过滤以 "a" 开头的水果
                .map(String::toUpperCase) // 将水果名称转为大写
                .distinct() // 去重
                .sorted() // 排序
                .collect(Collectors.toList()); // 收集到列表

        // 终止操作：遍历、计数
        result.forEach(System.out::println); // 遍历并打印
        long count = fruits.stream().filter(fruit -> fruit.startsWith("a")).count(); // 统计以 "a" 开头的水果数量
        System.out.println("Count: " + count);
    }
}
```

### Stream 的并行操作

Stream 支持并行操作，可以通过 `parallelStream` 方法将 Stream 转换为并行 Stream，从而利用多核处理器提高性能。

#### 示例：并行 Stream 操作

```java
import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apple");

        // 并行 Stream 操作
        fruits.parallelStream()
                .filter(fruit -> fruit.startsWith("a"))
                .map(String::toUpperCase)
                .distinct()
                .sorted()
                .forEach(System.out::println);
    }
}
```

### Stream 的常见用法

#### 1. 过滤

```java
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apple");
List<String> filteredFruits = fruits.stream()
        .filter(fruit -> fruit.startsWith("a"))
        .collect(Collectors.toList());
System.out.println(filteredFruits); // 输出：[apple, apple]
```

#### 2. 映射

```java
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
List<String> upperCaseFruits = fruits.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());
System.out.println(upperCaseFruits); // 输出：[APPLE, BANANA, CHERRY]
```

#### 3. 去重

```java
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "apple");
List<String> distinctFruits = fruits.stream()
        .distinct()
        .collect(Collectors.toList());
System.out.println(distinctFruits); // 输出：[apple, banana, cherry]
```

#### 4. 排序

```java
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
List<String> sortedFruits = fruits.stream()
        .sorted()
        .collect(Collectors.toList());
System.out.println(sortedFruits); // 输出：[apple, banana, cherry]
```

#### 5. 收集

```java
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
List<String> collectedFruits = fruits.stream()
        .collect(Collectors.toList());
System.out.println(collectedFruits); // 输出：[apple, banana, cherry]
```

#### 6. 计数

```java
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
long count = fruits.stream()
        .filter(fruit -> fruit.startsWith("a"))
        .count();
System.out.println(count); // 输出：2
```

#### 7. 归并

```java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
        .reduce(0, (a, b) -> a + b);
System.out.println(sum); // 输出：15
```

### 总结

`Stream` 是 Java 8 引入的一个强大功能，它提供了一种高级迭代器，允许你以声明式方式处理数据集合。通过合理使用 Stream 的中间操作和终止操作，可以编写出更简洁、更高效、更易读的代码。Stream 支持并行操作，可以通过 `parallelStream` 方法将 Stream 转换为并行 Stream，从而利用多核处理器提高性能。

# I/O流

Java中的I/O（输入/输出）流是用于处理数据输入和输出的机制。I/O流可以处理各种类型的数据，包括文件、网络传输、内存等。Java的I/O流主要分为两大类：字节流和字符流。

i/o的应用：1.读取配置文件，2.文件的上传，下载。3.网络传输 发消息，字符，数字，图片通通转成字节流，根据我们网络传输，字节流转成字符流进行读取

### 1. 字节流

字节流用于处理二进制数据，如图片、音频文件等。字节流的基类是`InputStream`和`OutputStream`。

#### 1.1 输入流（InputStream）

输入流用于从数据源读取数据。常见的输入流类包括：

- `FileInputStream`：从文件中读取数据。
- `ByteArrayInputStream`：从字节数组中读取数据。
- `BufferedInputStream`：提供缓冲功能，提高读取效率。//缓存区，如果我们读取大文件，
- `DataInputStream`：用于读取基本数据类型（如int、double等）的数据。

**示例代码：从文件中读取数据**

```java
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("example.txt")) {
            int content;
            while ((content = fis.read()) != -1) {
                System.out.print((char) content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

#### 1.2 输出流（OutputStream）

输出流用于向目标写入数据。常见的输出流类包括：

- `FileOutputStream`：向文件写入数据。
- `ByteArrayOutputStream`：向字节数组写入数据。
- BufferedOutputStream`：提供缓冲功能，提高写入效率。
- `DataOutputStream`：用于写入基本数据类型的数据。

**示例代码：向文件写入数据**

```java
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamExample {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("example.txt")) {
            String content = "Hello, World!";
            fos.write(content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### 2. 字符流

字符流用于处理文本数据，如文本文件等。字符流的基类是`Reader`和`Writer`。

#### 2.1 输入流（Reader）

输入流用于从数据源读取文本数据。常见的输入流类包括：

- `FileReader`：从文件中读取文本数据。
- `BufferedReader`：提供缓冲功能，提高读取效率。
- `InputStreamReader`：将字节流转换为字符流。

**示例代码：从文件中读取文本数据**

```java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

#### 2.2 输出流（Writer）

输出流用于向目标写入文本数据。常见的输出流类包括：

- `FileWriter`：向文件写入文本数据。
- `BufferedWriter`：提供缓冲功能，提高写入效率。
- `OutputStreamWriter`：将字节流转换为字符流。

**示例代码：向文件写入文本数据**

```java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterExample {
    public static void main(String[] args) {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("example.txt"))) {
            String content = "Hello, World!";
            bw.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### 3. 装饰器模式

Java I/O流采用了装饰器模式，允许通过组合多个流来增强功能。例如，`BufferedInputStream`和`BufferedOutputStream`可以为输入和输出流提供缓冲功能，从而提高读写效率。

**示例代码：使用缓冲流**

```java
import java.io.*;

public class BufferedStreamExample {
    public static void main(String[] args) {
        // 使用缓冲输入流
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("example.txt"))) {
            int content;
            while ((content = bis.read()) != -1) {
                System.out.print((char) content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 使用缓冲输出流
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("example.txt"))) {
            String content = "Hello, World!";
            bos.write(content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### 4. 序列化和反序列化

序列化是将对象转换为字节序列的过程，反序列化则是将字节序列还原为对象的过程。Java提供了`ObjectOutputStream`和`ObjectInputStream`来实现序列化和反序列化。

作用：持久化数据, 将数据存在磁盘或者是内存当中，网络传输

**示例代码：序列化和反序列化**

```java
import java.io.*;

public class SerializationExample {
    public static void main(String[] args) {
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"))) {
            Person person = new Person("Alice", 30);
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"))) {
            Person person = (Person) ois.readObject();
            System.out.println(person.getName() + ", " + person.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Person implements Serializable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
```

### 5. NIO（New I/O）

Java NIO是Java I/O的扩展，提供了非阻塞I/O操作、通道（Channel）和缓冲区（Buffer）等新特性。NIO的性能通常优于传统的I/O，适用于高性能的网络和文件I/O操作。

**示例代码：使用NIO读取文件**

```java
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class NIOExample {
    public static void main(String[] args) {
        try (FileChannel fileChannel = FileChannel.open(Paths.get("example.txt"), StandardOpenOption.READ)) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (fileChannel.read(buffer) != -1) {
                buffer.flip();
                while (buffer.hasRemaining()) {
                    System.out.print((char) buffer.get());
                }
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### 总结

Java I/O流提供了丰富的功能，用于处理各种数据输入和输出操作。字节流和字符流分别用于处理二进制数据和文本数据，装饰器模式允许通过组合多个流来增强功能。序列化和反序列化用于对象的持久化，NIO提供了高性能的I/O操作。通过合理使用这些I/O流，可以满足各种数据处理需求。

在Java中，文件操作是通过`java.io`包和`java.nio`包中的类和接口来实现的。这些类和接口提供了丰富的功能，用于读取、写入、创建、删除文件以及管理文件的元数据等。以下是一些常见的文件操作示例：

### 1. 创建文件

可以使用`File`类和`FileWriter`类来创建文件。

**示例代码：创建文件**

```java
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class CreateFileExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try (FileWriter writer = new FileWriter(filePath)) {
            writer.write("Hello, World!");
            System.out.println("文件创建成功，并写入内容");
        } catch (IOException e) {
            System.out.println("文件创建失败");
            e.printStackTrace();
        }
    }
}
```

### 2. 读取文件

可以使用`FileReader`和`BufferedReader`来读取文件内容。

**示例代码：读取文件**

```java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("文件读取失败");
            e.printStackTrace();
        }
    }
}
```

### 3. 写入文件

可以使用`FileWriter`和`BufferedWriter`来写入文件内容。

**示例代码：写入文件**

```java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class WriteFileExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) {
            writer.write("\nNew line added");
            System.out.println("内容写入成功");
        } catch (IOException e) {
            System.out.println("文件写入失败");
            e.printStackTrace();
        }
    }
}
```

### 4. 删除文件

可以使用`File`类的`delete`方法来删除文件。

**示例代码：删除文件**

```java
import java.io.File;

public class DeleteFileExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        File file = new File(filePath);
        if (file.delete()) {
            System.out.println("文件删除成功");
        } else {
            System.out.println("文件删除失败");
        }
    }
}
```

### 5. 检查文件是否存在

可以使用`File`类的`exists`方法来检查文件是否存在。

**示例代码：检查文件是否存在**

```java
import java.io.File;

public class CheckFileExistsExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        File file = new File(filePath);
        if (file.exists()) {
            System.out.println("文件存在");
        } else {
            System.out.println("文件不存在");
        }
    }
}
```

### 6. 获取文件信息

可以使用`File`类的方法来获取文件的元数据，如文件大小、最后修改时间等。

**示例代码：获取文件信息**

```java
import java.io.File;

public class GetFileInfoExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        File file = new File(filePath);
        if (file.exists()) {
            System.out.println("文件名: " + file.getName());
            System.out.println("文件路径: " + file.getAbsolutePath());
            System.out.println("文件大小: " + file.length() + " 字节");
            System.out.println("最后修改时间: " + file.lastModified());
        } else {
            System.out.println("文件不存在");
        }
    }
}
```

### 7. 创建目录

可以使用`File`类的`mkdir`和`mkdirs`方法来创建目录。

**示例代码：创建目录**

```java
import java.io.File;

public class CreateDirectoryExample {
    public static void main(String[] args) {
        String dirPath = "exampleDir";

        File dir = new File(dirPath);
        if (dir.mkdir()) {
            System.out.println("目录创建成功");
        } else {
            System.out.println("目录创建失败");
        }
    }
}
```

### 8. 遍历目录

可以使用`File`类的`listFiles`方法来遍历目录中的文件和子目录。

**示例代码：遍历目录**

```java
import java.io.File;

public class ListDirectoryExample {
    public static void main(String[] args) {
        String dirPath = "exampleDir";

        File dir = new File(dirPath);
        if (dir.exists() && dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    System.out.println(file.getName());
                }
            }
        } else {
            System.out.println("目录不存在");
        }
    }
}
```

### 9. 使用NIO操作文件

Java NIO提供了更高效的文件操作方式，如`Files`类和`Path`接口。

**示例代码：使用NIO创建文件**

```java
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.io.IOException;

public class NIOCreateFileExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        Path path = Paths.get(filePath);
        try {
            Files.createFile(path);
            System.out.println("文件创建成功");
        } catch (IOException e) {
            System.out.println("文件创建失败");
            e.printStackTrace();
        }
    }
}
```

**示例代码：使用NIO读取文件**

```java
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;

public class NIOWriteFileExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        Path path = Paths.get(filePath);
        try {
            String content = new String(Files.readAllBytes(path));
            System.out.println(content);
        } catch (IOException e) {
            System.out.println("文件读取失败");
            e.printStackTrace();
        }
    }
}
```

### 总结

Java提供了丰富的文件操作功能，通过`java.io`和`java.nio`包中的类和接口，可以实现文件的创建、读取、写入、删除、检查等操作。合理使用这些功能，可以满足各种文件处理需求。



在网络编程中，**Socket** 是一种非常重要的通信机制，用于在不同主机之间的应用程序之间建立通信。在Java中，可以通过`java.net.Socket`和`java.net.ServerSocket`类来实现基于TCP的网络通信。

### 1. 基本概念

- **Socket**：用于客户端和服务器之间的通信。客户端通过`Socket`连接到服务器，服务器通过`ServerSocket`监听客户端的连接请求。
- **TCP（传输控制协议）**：是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP保证数据的可靠传输，适用于需要高可靠性的应用，如文件传输、网页浏览等。

### 2. 服务器端编程

服务器端使用`ServerSocket`来监听客户端的连接请求。当客户端发起连接请求时，服务器端通过`accept`方法接受连接，并为每个客户端创建一个`Socket`对象。

**示例代码：服务器端**

```java
import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) {
        int port = 1234; // 服务器端口
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("服务器已启动，等待客户端连接...");

            // 接受客户端连接
            Socket clientSocket = serverSocket.accept();
            System.out.println("客户端已连接");

            // 获取输入流和输出流
            try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                 PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {

                // 从客户端读取数据
                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    System.out.println("客户端发送: " + inputLine);

                    // 向客户端发送响应
                    out.println("服务器响应: " + inputLine);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### 3. 客户端编程

客户端使用`Socket`连接到服务器。连接成功后，客户端可以通过`Socket`的输入流和输出流与服务器进行通信。

**示例代码：客户端**

```java
import java.io.*;
import java.net.*;

public class Client {
    public static void main(String[] args) {
        String host = "localhost"; // 服务器地址
        int port = 1234; // 服务器端口

        try (Socket socket = new Soc